package org.spring.spring;

import org.spring.spring.anno.*;
import org.spring.spring.aware.BeanNameAware;
import org.spring.spring.definition.BeanDefinition;
import org.spring.spring.definition.CurrentCreateBean;
import org.spring.spring.exception.SpringException;
import org.spring.spring.init.InitializingBean;
import org.spring.spring.processor.BeanPostProcessor;
import org.spring.spring.proxy.CglibProxyFactory;
import org.spring.spring.util.StringUtils;

import java.io.File;
import java.lang.reflect.*;
import java.net.URL;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * @projectName: sky-springboot
 * @package: org.sky.spring
 * @className: SkyApplicationContext
 * @author: zhl
 * @description: 容器应用上下文
 * @date: 2024/3/13 16:41
 * @version: 1.0
 */
public class SkyApplicationContext {

    private Class configClass;

    private static Object contextLock = new Object();

    private static final String SINGLETON = "singleton";

    /** 存放BeanDefinition对象 **/
    private static ConcurrentHashMap<String,BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();

    /** 存放单例bean对象（一级缓存） **/
    private static ConcurrentHashMap<String,Object> singletonBeanMap = new ConcurrentHashMap<>(256);

    /** 存放循环依赖中创建的代理对象（二级缓存） **/
    private static ConcurrentHashMap<String,Object> earlySingletonBeanMap = new ConcurrentHashMap<>(32);

    /** 存放lambld表达式（三级缓存） **/
    private static ConcurrentHashMap<String, CurrentCreateBean> singletonFactory = new ConcurrentHashMap<>(16);

    /** 存放processor的bean对象 **/
    private static ArrayList<BeanPostProcessor> processorBeanList = new ArrayList<>();

    /** 存放正在创建中的bean对象 **/
    private static Set<String> currentCreateBeanName = new HashSet<>();

    /** 存放每个类实现的接口 **/
    private static ConcurrentHashMap<Class<?>,List<Class<?>>> beanInterfaceMap = new ConcurrentHashMap<>();

    /** 已经创建国代理对象的bean **/
    private static Set<String> createProxy = new HashSet<>();

    /**
     * @param configClass:
     * @author zhl
     * @description 容器初始化：根据配置类扫描需要自动注入的包，自动生成bean对象
     * @date 2024/3/14 10:39
     */
    public SkyApplicationContext(Class configClass) {
        this.configClass = configClass;
        if (this.configClass.isAnnotationPresent(ComponentScan.class)) {
            // 获取该配置类的扫描路径
            ComponentScan annotation = (ComponentScan) this.configClass.getAnnotation(ComponentScan.class);
            String scanUrl = annotation.value();
            // 把包名替换成路径
            scanUrl = scanUrl.replace(".","/");

            // 使用类加载器加载该路径下面的所以文件
            ClassLoader classLoader = SkyApplicationContext.class.getClassLoader();
            // 获取扫描的包路径
            URL resource = classLoader.getResource(scanUrl);
            // 获取文件包
            File file = new File(resource.getFile());
            // 根据扫描包的class文件创建 BeanDefinition 对象
            createBeanDefinition(file, scanUrl, classLoader);

            // 创建单例bean对象
            for (Map.Entry<String, BeanDefinition> entry : beanDefinitionMap.entrySet()) {
                String beanName = entry.getKey();
                BeanDefinition definition = entry.getValue();
                if (SINGLETON.equals(definition.getScope())){
                    Object object = createBean(beanName, definition);
                    // 存放入单例bean集合(一级缓存
                    singletonBeanMap.put(beanName,object);
                }
            }
        }
    }

    /**
     * @param beanName:
     * @param beanDefinition:
     * @return Object
     * @author zhl
     * @description 根据bean定义对象 BeanDefinition 创建bean对象
     * @date 2024/3/14 11:23
     */
    private Object createBean(String beanName, BeanDefinition beanDefinition) {
        Object singleton = singletonBeanMap.get(beanName);
        if (singleton != null){
            return singleton;
        }
        // 把正在创建的bean对像进行备注
        currentCreateBeanName.add(beanName);
        Class clazz = beanDefinition.getType();
        try {
            // 调用无参构造方法构造类
            Object instance = clazz.getConstructor().newInstance();
            // 添加到三级缓存
            addSingletonFactory(beanName,new CurrentCreateBean(instance,clazz));

            Object exposedObject = instance;
            // 属性填充
            Map<String, Object> classObjectMap = populateBean(clazz);
            // 初始化
            exposedObject = initializeBean(beanName, exposedObject, classObjectMap);

            // Aware 回调
            if (exposedObject instanceof BeanNameAware){
                ((BeanNameAware) exposedObject).setBeanName(beanName);
            }

            // 把二级缓存的单例对象（代理对象）放入到一级缓存中,并从二级缓存中删除
            Object object = earlySingletonBeanMap.remove(beanName);
            if (object != null){
                if (exposedObject == instance){
                    exposedObject = object;
                }
            }
            currentCreateBeanName.remove(beanName);

            return exposedObject;
        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        } catch (InvocationTargetException e) {
            throw new RuntimeException(e);
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        }
    }

    private Object initializeBean(String beanName, Object exposedObject, Map<String, Object> classObjectMap) {
        Object singleton = singletonBeanMap.get(beanName);
        if (singleton != null){
            return singleton;
        }
        Object bean = exposedObject;
        Object object = earlySingletonBeanMap.get(beanName);
        // 初始化中
        Field[] declaredFields = exposedObject.getClass().getDeclaredFields();
        if (object != null){
            exposedObject = object;
        }

        // 初始化前进行processor处理
        for (BeanPostProcessor processor : processorBeanList) {
            // 返回的代理对象
            exposedObject = processor.postProcessorBeforeInitialization(beanName,exposedObject  );
        }

        // 初始化中
        for (Field declaredField : declaredFields) {
            declaredField.setAccessible(true);
            String name = declaredField.getName();
            Object filedObject;
            if (name.equals(beanName)){
                // 依赖注入了本身
                filedObject = exposedObject;
            } else {
                filedObject = classObjectMap.get(name);
            }
            if (filedObject == null){
                try {
                    filedObject = declaredField.get(bean);
                } catch (IllegalAccessException e) {
                    throw new RuntimeException(e);
                }
            }

            try {
                declaredField.set(exposedObject,filedObject);
            } catch (IllegalAccessException e) {
                throw new RuntimeException(e);
            }
        }
        if (exposedObject instanceof InitializingBean){
            ((InitializingBean) exposedObject).afterPropertiesSet();
        }

        // 初始化后进行processor处理
        for (BeanPostProcessor processor : processorBeanList) {
            // 判断该对象是否需要进行aop处理（循环依赖中已经生成了的代理对象需要排除）
            exposedObject = processor.postProcessorAfterInitialization(beanName,exposedObject,createProxy);
        }

        return exposedObject;
    }

    /**
     * @param clazz:
     * @return void
     * @author zhl
     * @description 填充属性
     * @date 2024/3/20 15:15
     */
    private Map<String,Object> populateBean(Class clazz) throws InstantiationException, IllegalAccessException {
        // 属性填充
        Field[] fields = clazz.getDeclaredFields();
        Map<String,Object> paramMap = new HashMap<>(fields.length);
        for (Field field : fields) {
            if (field.isAnnotationPresent(Resource.class)) {
                // 获取属性名称
                String fieldName = field.getName();
                // 获取属性类型
                Class<?> fieldType = field.getType();
                // 先从单例集合中获取该属性的bean对象，如果不存在则创建bean对象
                Object object = singletonBeanMap.get(fieldName);
                if (Objects.isNull(object)){
                    // 判断属性是不是接口，是接口，则需要填充它的实现类(使用jdk动态代理的方式)
                    // proxyByJDK(clazz, fieldType, object, fieldName);

                    // 自动注入的bean对象
                    object = autowiredBean(clazz, fieldType, fieldName);
                    if (Objects.isNull(object)){
                        throw new SpringException("依赖注入失败！！");
                    }

                }
                paramMap.put(fieldName,object);
            }
        }
        return paramMap;
        // 可能会有循环依赖（）循环依赖会返回代理对象
    }

    private void proxyByJDK(Class clazz, Class<?> fieldType, Object object, String fieldName) throws InstantiationException, IllegalAccessException {
        if (fieldType.isInterface()) {
            // 获取接口的全限定名
            String typeName = fieldType.getName();
            // 把扫描到的所有类遍历，找到实现该接口的类
            List<Map.Entry<Class<?>, List<Class<?>>>> collect = beanInterfaceMap.entrySet().stream().filter(entity -> {
                List<Class<?>> list = entity.getValue();
                // 遍历所有类的所有接口，找到全限定名一致的接口，则说明该类一定实现了该接口
                List<String> className = list.stream().map(data -> data.getName()).collect(Collectors.toList());
                if (className.contains(typeName)){
                    return true;
                }
                return false;
            }).collect(Collectors.toList());
            if (StringUtils.isNotEmpty(collect)){
                // 可能多个类实现了该接口（按照约定俗成，每个业务接口只有一个实现类）
                Class<?> aClass = collect.get(0).getKey();
                // 通过该接口的实现类去实例化该接口
                object = autowiredBean(clazz, aClass, fieldName);
            }
            if (Objects.isNull(object)){
                throw new SpringException("需要aop的对象依赖注入失败！！");
            }
        } else {
            // 自动注入的bean对象
            object = autowiredBean(clazz, fieldType, fieldName);
            if (Objects.isNull(object)){
                throw new SpringException("普通对象依赖注入失败！！");
            }
        }
    }

    /**
     * @param clazz:
     * @param fieldType:
     * @param fieldName:
     * @return Object
     * @author zhl
     * @description 自动注入bean对象
     * @date 2024/3/20 15:14
     */
    private Object autowiredBean(Class clazz, Class<?> fieldType, String fieldName) throws InstantiationException, IllegalAccessException {
        Object object;
        if (fieldType.isAnnotationPresent(Component.class)) {
            // 判断该对象是否在创建中
            if (currentCreateBeanName.contains(fieldName)){
                object = circularDependency(fieldName);
            }
            else {
                // 获取属性模式
                String scope = SINGLETON;
                if (fieldType.isAnnotationPresent(Scope.class)) {
                    Scope scopeAnnotation = fieldType.getAnnotation(Scope.class);
                    String value = scopeAnnotation.value();
                    if (StringUtils.isNotEmpty(value)){
                        scope = value;
                    }
                }
                // 定义属性的 BeanDefinition
                BeanDefinition definition = new BeanDefinition();
                definition.setType(fieldType);
                definition.setScope(scope);
                beanDefinitionMap.put(fieldName,definition);

                // 创建属性bean对象
                object = createBean(fieldName, definition);
                singletonBeanMap.put(fieldName,object);
            }
        } else {
            // 不需要依赖注入
            object = clazz.newInstance();
        }
        return object;
    }

    /**
     * @param fieldName:
     * @return Object
     * @author zhl
     * @description 解决循环依赖
     * @date 2024/3/20 15:14
     */
    private Object circularDependency(String fieldName){
        // 出现循环依赖问题,创建代理对象，将代理对象放入二级缓存（放入二级缓存的目的是多个bean对象对此对象出现循环依赖问题时，依旧保持单例模式，否者就会创建多个代理对象，不符合单例的定义）
        Object object = singletonBeanMap.get(fieldName);
        if (object == null){
            // 尝试从二级缓存中获取
            object = earlySingletonBeanMap.get(fieldName);
            if (object == null){
                // 双检索，防止多线程bug
                synchronized (contextLock){
                    object = singletonBeanMap.get(fieldName);
                    if (object == null){
                        // 尝试从二级缓存中获取
                        object = earlySingletonBeanMap.get(fieldName);
                        if (object == null){
                            // 二级缓存中仍然没有，则尝试从三级缓存中获取（二级缓存并不真的生成代理对象，所以需要三级缓存来真正打破循环，生成代理对象）
                            CurrentCreateBean currentCreateBean = singletonFactory.get(fieldName);
                            // 生成代理对象(UserService 的属性 ApiService 的代理对象)，打破循环
                            Class clazz = currentCreateBean.getClazz();
                            if (clazz.isAnnotationPresent(Aspect.class)) {
                                // 需要判断是否需要进行aop，需要进行aop才需要生成代理对象，不需要进行aop，则直接把三级缓存中的普通对象放到二级缓存中去
                                Object proxyInstance = CglibProxyFactory.getProxyInstance(currentCreateBean.getClazz(),currentCreateBean.getTarget());
                                object = proxyInstance;
                                createProxy.add(fieldName);
                            } else {
                                object = currentCreateBean.getTarget();
                            }
                            // 把生成的代理对象添加到二级缓存
                            earlySingletonBeanMap.put(fieldName,object);
                            // 从三级缓存中移除循环依赖的对象
                            singletonFactory.remove(fieldName);
                        }
                    }
                }
            }
        }
        return object;
        // throw new SpringException("出现循环依赖问题");
    }

    /**
     * @param beanName:
     * @param currentCreateBean:
     * @return void
     * @author zhl
     * @description 实例化bean对象时，添加到三级缓存
     * @date 2024/3/20 15:13
     */
    private void addSingletonFactory(String beanName, CurrentCreateBean currentCreateBean) {
        synchronized (contextLock){
            singletonFactory.put(beanName,currentCreateBean);
            earlySingletonBeanMap.remove(beanName);
        }
    }


    /**
     * @param file: 扫描包
     * @param scanUrl: 配置的扫描包路径
     * @param classLoader: 类加载器
     * @return void
     * @author zhl
     * @description 根据扫描包的class文件创建 BeanDefinition 对象
     * @date 2024/3/14 11:18
     */
    private static void createBeanDefinition(File file, String scanUrl, ClassLoader classLoader) {
        scanUrl = scanUrl.replace("/","\\");
        if (file.isDirectory()) {
            // 获取文件夹下面的所有文件
            File[] files = file.listFiles();
            for (File packageFile : files) {
                // 只对文件进行处理，文件夹应该进行递归处理
                if (packageFile.isFile() ) {
                    // 获取文件的全路径名
                    String absolutePath = packageFile.getAbsolutePath();
                    // 只需要class文件
                    if (absolutePath.endsWith(".class")) {
                        int indexOf = absolutePath.indexOf(scanUrl);
                        // class文件相对路径
                        String substring = absolutePath.substring(indexOf);
                        // 路径名转为包名
                        String packageName = substring.replace("\\", ".");
                        // 获取类的全路径名称
                        String className = packageName.replace(".class", "");
                        try {
                            Class<?> clazz = classLoader.loadClass(className);
                            // 获取所有类的实现接口
                            Class<?>[] interfaces = clazz.getInterfaces();
                            beanInterfaceMap.put(clazz,Arrays.asList(interfaces));
                            if (clazz.isAnnotationPresent(Component.class)) {
                                // 对processor进行处理,判断某个类是否实现某个接口
                                if (BeanPostProcessor.class.isAssignableFrom(clazz)){
                                    processorBeanList.add((BeanPostProcessor) clazz.newInstance());
                                }
                                // 判断该类文件是否需要自动注入
                                Component component = clazz.getAnnotation(Component.class);
                                // 默认为单例bean
                                String scopeType = SINGLETON;
                                if (clazz.isAnnotationPresent(Scope.class)) {
                                    // 获取bean的模式
                                    Scope scopeAnnotation = clazz.getAnnotation(Scope.class);
                                    String value = scopeAnnotation.value();
                                    if (StringUtils.isNotEmpty(scopeType)){
                                        scopeType = value;
                                    }
                                }
                                // 该bean对象别名
                                String beanName = component.value();
                                if (StringUtils.isEmpty(beanName)){
                                    // 如果未设置别名，则类名首字母转小写，变为beanName
                                    String simpleName = clazz.getSimpleName();
                                    beanName = StringUtils.getMethodName(simpleName);
                                }
                                BeanDefinition beanDefinition = new BeanDefinition();
                                beanDefinition.setType(clazz);
                                beanDefinition.setScope(scopeType);
                                beanDefinitionMap.put(beanName,beanDefinition);
                            }
                        } catch (ClassNotFoundException e) {
                            throw new RuntimeException(e);
                        } catch (Exception e) {
                            throw new RuntimeException(e);
                        }
                    }
                } else {
                    // 如果是包，则还需要往下面加载
                    String fileName = packageFile.getName();
                    String newPackageUrl = scanUrl + "\\" + fileName;
                    createBeanDefinition(packageFile,newPackageUrl,classLoader);
                }
            }
        }
    }

    /**
     * @param beanName: bean对象名称
     * @return Object
     * @author zhl
     * @description 根据名称获取bean对象
     * @date 2024/3/14 10:29
     */
    public Object getBean(String beanName) {
        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        if (Objects.isNull(beanDefinition)){
            throw new NullPointerException("bean对象定义异常！");
        }
        if (SINGLETON.equals(beanDefinition.getScope())){
            // 单例模式
            Object object = singletonBeanMap.get(beanName);
            if (Objects.isNull(object)){
                object = createBean(beanName,beanDefinition);
                // 存放入单例bean集合
                singletonBeanMap.put(beanName,object);
            }
            return object;
        } else {
            // 多例模式
            return createBean(beanName,beanDefinition);
        }
    }
}
